Filtering for -keepattributes rules
Change-Id: I21ebaf47d3943bbf50d4191be653a5bc88f92691
diff --git a/src/main/java/com/android/tools/r8/processkeeprules/FilteredKeepRulesBuilder.java b/src/main/java/com/android/tools/r8/processkeeprules/FilteredKeepRulesBuilder.java
index 8e4e552..2831df5 100644
--- a/src/main/java/com/android/tools/r8/processkeeprules/FilteredKeepRulesBuilder.java
+++ b/src/main/java/com/android/tools/r8/processkeeprules/FilteredKeepRulesBuilder.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.shaking.ProguardConfigurationParser.ProguardConfigurationSourceParser;
 import com.android.tools.r8.shaking.ProguardConfigurationParserConsumer;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.shaking.ProguardPathList;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
 import com.android.tools.r8.utils.Reporter;
@@ -180,8 +181,26 @@
       ProguardConfigurationSourceParser parser,
       Position position,
       TextPosition positionStart) {
-    ensureNewlineAfterComment();
-    write(parser, positionStart);
+    ProguardKeepAttributes keepAttributes = ProguardKeepAttributes.fromPatterns(attributesPatterns);
+    if (keepAttributes.lineNumberTable
+        || keepAttributes.runtimeInvisibleAnnotations
+        || keepAttributes.runtimeInvisibleParameterAnnotations
+        || keepAttributes.runtimeInvisibleTypeAnnotations
+        || keepAttributes.sourceFile) {
+      // Comment out the -keepattributes rule.
+      writeComment(parser, positionStart);
+      // Unset the undesired attributes and expand the rule.
+      keepAttributes.lineNumberTable = false;
+      keepAttributes.runtimeInvisibleAnnotations = false;
+      keepAttributes.runtimeInvisibleParameterAnnotations = false;
+      keepAttributes.runtimeInvisibleTypeAnnotations = false;
+      keepAttributes.sourceFile = false;
+      ensureNewlineAfterComment();
+      write(keepAttributes.toString());
+    } else {
+      ensureNewlineAfterComment();
+      write(parser, positionStart);
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 8697204..0f36f87 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -1970,6 +1970,9 @@
       if (isQuote(quote)) {
         expectClosingQuote(quote);
       }
+      int lastPatternEndLine = line;
+      int lastPatternEndLineStartPosition = lineStartPosition;
+      int lastPatternEndPosition = position;
       while (pattern != null) {
         patterns.add(pattern);
         skipWhitespace();
@@ -1981,17 +1984,24 @@
           if (isQuote(quote)) {
             expectClosingQuote(quote);
           }
+          lastPatternEndLine = line;
+          lastPatternEndLineStartPosition = lineStartPosition;
+          lastPatternEndPosition = position;
           if (pattern == null) {
             throw parseError("Expected list element", start);
           }
         } else {
-          pattern = null;
+          break;
         }
       }
       skipWhitespace();
       if (!eof() && !hasNextChar('-') && !hasNextChar('@')) {
         throw parseError("Unexpected attribute");
       }
+      // Position the parser at the end of the rule before notifying the configuration consumer.
+      line = lastPatternEndLine;
+      lineStartPosition = lastPatternEndLineStartPosition;
+      position = lastPatternEndPosition;
       return patterns;
     }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
index cf407e6..e0848b0 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
@@ -224,60 +224,58 @@
 
   public StringBuilder append(StringBuilder builder) {
     List<String> attributes = new ArrayList<>();
-    if (sourceFile) {
-      attributes.add(SOURCE_FILE);
-    }
-    if (innerClasses) {
-      attributes.add(INNER_CLASSES);
+    if (annotationDefault) {
+      attributes.add(ANNOTATION_DEFAULT);
     }
     if (enclosingMethod) {
       attributes.add(ENCLOSING_METHOD);
     }
-    if (signature) {
-      attributes.add(SIGNATURE);
-    }
     if (exceptions) {
       attributes.add(EXCEPTIONS);
     }
+    if (innerClasses) {
+      attributes.add(INNER_CLASSES);
+    }
     if (methodParameters) {
       attributes.add(METHOD_PARAMETERS);
     }
-    if (sourceDebugExtension) {
-      attributes.add(SOURCE_DEBUG_EXTENSION);
-    }
-    if (runtimeVisibleAnnotations) {
-      attributes.add(RUNTIME_VISIBLE_ANNOTATIONS);
+    if (permittedSubclasses) {
+      attributes.add(PERMITTED_SUBCLASSES);
     }
     if (runtimeInvisibleAnnotations) {
       attributes.add(RUNTIME_INVISIBLE_ANNOTATIONS);
     }
-    if (runtimeVisibleParameterAnnotations) {
-      attributes.add(RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS);
-    }
     if (runtimeInvisibleParameterAnnotations) {
       attributes.add(RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS);
     }
-    if (runtimeVisibleTypeAnnotations) {
-      attributes.add(RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
-    }
     if (runtimeInvisibleTypeAnnotations) {
       attributes.add(RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
     }
-    if (annotationDefault) {
-      attributes.add(ANNOTATION_DEFAULT);
+    if (runtimeVisibleAnnotations) {
+      attributes.add(RUNTIME_VISIBLE_ANNOTATIONS);
+    }
+    if (runtimeVisibleParameterAnnotations) {
+      attributes.add(RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS);
+    }
+    if (runtimeVisibleTypeAnnotations) {
+      attributes.add(RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
+    }
+    if (signature) {
+      attributes.add(SIGNATURE);
+    }
+    if (sourceDebugExtension) {
+      attributes.add(SOURCE_DEBUG_EXTENSION);
+    }
+    if (sourceFile) {
+      attributes.add(SOURCE_FILE);
     }
     if (stackMapTable) {
       attributes.add(STACK_MAP_TABLE);
     }
-    if (permittedSubclasses) {
-      attributes.add(PERMITTED_SUBCLASSES);
-    }
-
-    if (attributes.size() > 0) {
+    if (!attributes.isEmpty()) {
       builder.append("-keepattributes ");
       builder.append(String.join(",", attributes));
     }
-
     return builder;
   }
 
diff --git a/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesCommandTest.java b/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesCommandTest.java
index 92e2034..92fc96a 100644
--- a/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesCommandTest.java
+++ b/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesCommandTest.java
@@ -8,7 +8,6 @@
 import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
 import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.equalTo;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import com.android.tools.r8.CompilationFailedException;
@@ -115,20 +114,7 @@
     }
 
     // Rerun validation after filtering. This should succeed without diagnostics.
-    String filteredRules = filter(rules, origin);
-    boolean hasKeepAttributes = rules.contains("-keepattributes");
-    try {
-      validate(
-          filteredRules,
-          origin,
-          diagnostics -> {
-            if (!hasKeepAttributes) {
-              diagnostics.assertNoMessages();
-            }
-          });
-    } catch (CompilationFailedException e) {
-      assertTrue(hasKeepAttributes);
-    }
+    validate(filter(rules, origin), origin, TestDiagnosticMessagesImpl::assertNoMessages);
   }
 
   private String filter(String rules, Origin origin) throws CompilationFailedException {
diff --git a/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesFilteringTest.java b/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesFilteringTest.java
index 9ce869b..bd1cde6 100644
--- a/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesFilteringTest.java
+++ b/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesFilteringTest.java
@@ -36,8 +36,8 @@
     String keepRules =
         StringUtils.unixLines(
             " -dontobfuscate -dontoptimize -dontshrink -keep class com.example.MainActivity # keep",
-            "# Keep all attributes",
-            "-keepattributes *",
+            "# Keep all annotations",
+            "-keepattributes AnnotationDefault,*Annotations*",
             "# Keep all",
             "-keep  class **",
             "# Multi line repackageclasses",
@@ -55,8 +55,10 @@
         StringUtils.unixLines(
             " #-dontobfuscate -dontoptimize -dontshrink ",
             "-keep class com.example.MainActivity # keep",
-            "# Keep all attributes",
-            "-keepattributes *",
+            "# Keep all annotations",
+            "#-keepattributes AnnotationDefault,*Annotations*",
+            "-keepattributes"
+                + " AnnotationDefault,RuntimeVisibleAnnotations,RuntimeVisibleParameterAnnotations,RuntimeVisibleTypeAnnotations",
             "# Keep all",
             "-keep  class **",
             "# Multi line repackageclasses",
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 7c65203..5d1c257 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -857,7 +857,7 @@
     verifyParserEndsCleanly();
     ProguardConfiguration config = builder.build();
     assertEquals(
-        "-keepattributes RuntimeVisibleAnnotations,RuntimeInvisibleAnnotations",
+        "-keepattributes RuntimeInvisibleAnnotations,RuntimeVisibleAnnotations",
         config.getKeepAttributes().toString());
     assertEquals(
         StringUtils.joinLines("-keep class kotlin.Metadata {", "  *;", "}"),