Ignore _NONNULL_ attributes in keep rules

Bug: 186529489
Change-Id: I9c07ea2a74312865fe6ca4d2ea38cdfb6bc814ff
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 2871b74..ff71812 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -81,6 +81,8 @@
   private static final List<String> IGNORED_CLASS_DESCRIPTOR_OPTIONS =
       ImmutableList.of("isclassnamestring", "whyarenotsimple", "convertchecknotnull");
 
+  private static final List<String> IGNORED_RETURN_VALUE_ATTRIBUTES = ImmutableList.of("_NONNULL_");
+
   private static final List<String> WARNED_SINGLE_ARG_OPTIONS = ImmutableList.of(
       // TODO(b/37137994): -outjars should be reported as errors, not just as warnings!
       "outjars");
@@ -588,6 +590,19 @@
           || parseOptimizationOption(optionStart);
     }
 
+    private boolean parseIgnoredReturnValueAttribute() {
+      return Iterables.any(IGNORED_RETURN_VALUE_ATTRIBUTES, this::skipReturnValueAttribute);
+    }
+
+    private boolean parseIgnoredReturnValueAttributes() {
+      boolean anySkipped = false;
+      while (parseIgnoredReturnValueAttribute()) {
+        anySkipped = true;
+        skipWhitespace();
+      }
+      return anySkipped;
+    }
+
     private void parseInclude() throws ProguardRuleParserException {
       TextPosition start = getPosition();
       Path included = parseFileName(false);
@@ -673,6 +688,10 @@
       return false;
     }
 
+    private boolean skipReturnValueAttribute(String name) {
+      return acceptString(name);
+    }
+
     private boolean parseOptimizationOption(TextPosition optionStart)
         throws ProguardRuleParserException {
       if (!acceptString("optimizations")) {
@@ -1317,49 +1336,52 @@
                   // Parse "return ..." if present.
                   if (acceptString("return")) {
                     skipWhitespace();
-                    if (acceptString("true")) {
-                      ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(true));
-                    } else if (acceptString("false")) {
-                      ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(false));
-                    } else if (acceptString("null")) {
-                      ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue());
-                    } else {
-                      TextPosition fieldOrValueStart = getPosition();
-                      String qualifiedFieldNameOrInteger = acceptFieldNameOrIntegerForReturn();
-                      if (qualifiedFieldNameOrInteger != null) {
-                        if (isInteger(qualifiedFieldNameOrInteger)) {
-                          Integer min = Integer.parseInt(qualifiedFieldNameOrInteger);
-                          Integer max = min;
-                          skipWhitespace();
-                          if (acceptString("..")) {
+                    boolean anyReturnValueAttributesSkipped = parseIgnoredReturnValueAttributes();
+                    if (!anyReturnValueAttributesSkipped || !hasNextChar(';')) {
+                      if (acceptString("true")) {
+                        ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(true));
+                      } else if (acceptString("false")) {
+                        ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(false));
+                      } else if (acceptString("null")) {
+                        ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue());
+                      } else {
+                        TextPosition fieldOrValueStart = getPosition();
+                        String qualifiedFieldNameOrInteger = acceptFieldNameOrIntegerForReturn();
+                        if (qualifiedFieldNameOrInteger != null) {
+                          if (isInteger(qualifiedFieldNameOrInteger)) {
+                            Integer min = Integer.parseInt(qualifiedFieldNameOrInteger);
+                            Integer max = min;
                             skipWhitespace();
-                            max = acceptInteger();
-                            if (max == null) {
-                              throw parseError("Expected integer value");
+                            if (acceptString("..")) {
+                              skipWhitespace();
+                              max = acceptInteger();
+                              if (max == null) {
+                                throw parseError("Expected integer value");
+                              }
                             }
-                          }
-                          if (!allowValueSpecification) {
-                            throw parseError("Unexpected value specification", fieldOrValueStart);
-                          }
-                          ruleBuilder.setReturnValue(
-                              new ProguardMemberRuleReturnValue(new LongInterval(min, max)));
-                        } else {
-                          if (ruleBuilder.getTypeMatcher() instanceof MatchSpecificType) {
-                            int lastDotIndex = qualifiedFieldNameOrInteger.lastIndexOf(".");
-                            DexType fieldType = ((MatchSpecificType) ruleBuilder
-                                .getTypeMatcher()).type;
-                            DexType fieldClass =
-                                dexItemFactory.createType(
-                                    javaTypeToDescriptor(
-                                        qualifiedFieldNameOrInteger.substring(0, lastDotIndex)));
-                            DexString fieldName =
-                                dexItemFactory.createString(
-                                    qualifiedFieldNameOrInteger.substring(lastDotIndex + 1));
-                            DexField field = dexItemFactory
-                                .createField(fieldClass, fieldType, fieldName);
-                            ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(field));
+                            if (!allowValueSpecification) {
+                              throw parseError("Unexpected value specification", fieldOrValueStart);
+                            }
+                            ruleBuilder.setReturnValue(
+                                new ProguardMemberRuleReturnValue(new LongInterval(min, max)));
                           } else {
-                            throw parseError("Expected specific type", fieldOrValueStart);
+                            if (ruleBuilder.getTypeMatcher() instanceof MatchSpecificType) {
+                              int lastDotIndex = qualifiedFieldNameOrInteger.lastIndexOf(".");
+                              DexType fieldType =
+                                  ((MatchSpecificType) ruleBuilder.getTypeMatcher()).type;
+                              DexType fieldClass =
+                                  dexItemFactory.createType(
+                                      javaTypeToDescriptor(
+                                          qualifiedFieldNameOrInteger.substring(0, lastDotIndex)));
+                              DexString fieldName =
+                                  dexItemFactory.createString(
+                                      qualifiedFieldNameOrInteger.substring(lastDotIndex + 1));
+                              DexField field =
+                                  dexItemFactory.createField(fieldClass, fieldType, fieldName);
+                              ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(field));
+                            } else {
+                              throw parseError("Expected specific type", fieldOrValueStart);
+                            }
                           }
                         }
                       }
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 f49dc97..c93200c 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -442,8 +442,8 @@
     assertEquals(1, assumeNoSideEffects.size());
     int matches = 0;
     for (ProguardMemberRule rule : assumeNoSideEffects.get(0).getMemberRules()) {
-      assertTrue(rule.hasReturnValue());
       if (rule.getName().matches("returnsTrue") || rule.getName().matches("returnsFalse")) {
+        assertTrue(rule.hasReturnValue());
         assertTrue(rule.getReturnValue().isBoolean());
         assertFalse(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
@@ -451,6 +451,7 @@
         assertEquals(rule.getName().matches("returnsTrue"), rule.getReturnValue().getBoolean());
         matches |= 1 << 0;
       } else if (rule.getName().matches("returns1")) {
+        assertTrue(rule.hasReturnValue());
         assertFalse(rule.getReturnValue().isBoolean());
         assertTrue(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
@@ -461,6 +462,7 @@
         assertEquals(1, rule.getReturnValue().getSingleValue());
         matches |= 1 << 1;
       } else if (rule.getName().matches("returns2To4")) {
+        assertTrue(rule.hasReturnValue());
         assertFalse(rule.getReturnValue().isBoolean());
         assertTrue(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
@@ -470,6 +472,7 @@
         assertEquals(4, rule.getReturnValue().getValueRange().getMax());
         matches |= 1 << 2;
       } else if (rule.getName().matches("returns234To567")) {
+        assertTrue(rule.hasReturnValue());
         assertFalse(rule.getReturnValue().isBoolean());
         assertTrue(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
@@ -479,6 +482,7 @@
         assertEquals(567, rule.getReturnValue().getValueRange().getMax());
         matches |= 1 << 3;
       } else if (rule.getName().matches("returnsField")) {
+        assertTrue(rule.hasReturnValue());
         assertFalse(rule.getReturnValue().isBoolean());
         assertFalse(rule.getReturnValue().isValueRange());
         assertTrue(rule.getReturnValue().isField());
@@ -488,17 +492,31 @@
         assertEquals("X", rule.getReturnValue().getField().name.toString());
         matches |= 1 << 4;
       } else if (rule.getName().matches("returnsNull")) {
+        assertTrue(rule.hasReturnValue());
         assertFalse(rule.getReturnValue().isBoolean());
         assertFalse(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
         assertTrue(rule.getReturnValue().isNull());
         assertTrue(rule.getReturnValue().isSingleValue());
         matches |= 1 << 5;
+      } else if (rule.getName().matches("returnsNonNull")) {
+        assertFalse(rule.hasReturnValue());
+        matches |= 1 << 6;
+      } else if (rule.getName().matches("returnsNonNullField")) {
+        assertTrue(rule.hasReturnValue());
+        assertFalse(rule.getReturnValue().isBoolean());
+        assertFalse(rule.getReturnValue().isValueRange());
+        assertTrue(rule.getReturnValue().isField());
+        assertFalse(rule.getReturnValue().isNull());
+        assertEquals("com.google.C", rule.getReturnValue().getField().holder.toString());
+        assertEquals("Object", rule.getReturnValue().getField().type.toString());
+        assertEquals("X", rule.getReturnValue().getField().name.toString());
+        matches |= 1 << 7;
       } else {
         fail("Unexpected");
       }
     }
-    assertEquals((1 << 6) - 1, matches);
+    assertEquals((1 << 8) - 1, matches);
   }
 
   @Test
@@ -511,8 +529,8 @@
     assertEquals(1, assumeValues.size());
     int matches = 0;
     for (ProguardMemberRule rule : assumeValues.get(0).getMemberRules()) {
-      assertTrue(rule.hasReturnValue());
       if (rule.getName().matches("isTrue") || rule.getName().matches("isFalse")) {
+        assertTrue(rule.hasReturnValue());
         assertTrue(rule.getReturnValue().isBoolean());
         assertFalse(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
@@ -520,6 +538,7 @@
         assertEquals(rule.getName().matches("isTrue"), rule.getReturnValue().getBoolean());
         matches |= 1 << 0;
       } else if (rule.getName().matches("is1")) {
+        assertTrue(rule.hasReturnValue());
         assertFalse(rule.getReturnValue().isBoolean());
         assertTrue(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
@@ -530,6 +549,7 @@
         assertEquals(1, rule.getReturnValue().getSingleValue());
         matches |= 1 << 1;
       } else if (rule.getName().matches("is2To4")) {
+        assertTrue(rule.hasReturnValue());
         assertFalse(rule.getReturnValue().isBoolean());
         assertTrue(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
@@ -539,6 +559,7 @@
         assertEquals(4, rule.getReturnValue().getValueRange().getMax());
         matches |= 1 << 2;
       } else if (rule.getName().matches("is234To567")) {
+        assertTrue(rule.hasReturnValue());
         assertFalse(rule.getReturnValue().isBoolean());
         assertTrue(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
@@ -548,6 +569,7 @@
         assertEquals(567, rule.getReturnValue().getValueRange().getMax());
         matches |= 1 << 3;
       } else if (rule.getName().matches("isField")) {
+        assertTrue(rule.hasReturnValue());
         assertFalse(rule.getReturnValue().isBoolean());
         assertFalse(rule.getReturnValue().isValueRange());
         assertTrue(rule.getReturnValue().isField());
@@ -557,17 +579,31 @@
         assertEquals("X", rule.getReturnValue().getField().name.toString());
         matches |= 1 << 4;
       } else if (rule.getName().matches("isNull")) {
+        assertTrue(rule.hasReturnValue());
         assertFalse(rule.getReturnValue().isBoolean());
         assertFalse(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
         assertTrue(rule.getReturnValue().isNull());
         assertTrue(rule.getReturnValue().isSingleValue());
         matches |= 1 << 5;
+      } else if (rule.getName().matches("returnsNonNull")) {
+        assertFalse(rule.hasReturnValue());
+        matches |= 1 << 6;
+      } else if (rule.getName().matches("returnsNonNullField")) {
+        assertTrue(rule.hasReturnValue());
+        assertFalse(rule.getReturnValue().isBoolean());
+        assertFalse(rule.getReturnValue().isValueRange());
+        assertTrue(rule.getReturnValue().isField());
+        assertFalse(rule.getReturnValue().isNull());
+        assertEquals("com.google.C", rule.getReturnValue().getField().holder.toString());
+        assertEquals("Object", rule.getReturnValue().getField().type.toString());
+        assertEquals("X", rule.getReturnValue().getField().name.toString());
+        matches |= 1 << 7;
       } else {
         fail("Unexpected");
       }
     }
-    assertEquals((1 << 6) - 1, matches);
+    assertEquals((1 << 8) - 1, matches);
   }
 
   @Test
diff --git a/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags b/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags
index b233ea3..d63e2e1 100644
--- a/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags
+++ b/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags
@@ -10,4 +10,6 @@
     public static int returns234To567() return 234..567;
     public static int returnsField() return com.google.C.X;
     public static Object returnsNull() return null;
+    public static Object returnsNonNull() return _NONNULL_;
+    public static Object returnsNonNullField() return _NONNULL_ com.google.C.X;
 }
diff --git a/src/test/proguard/valid/assume-values-with-return-value.flags b/src/test/proguard/valid/assume-values-with-return-value.flags
index 90cefbe..fdb6a25 100644
--- a/src/test/proguard/valid/assume-values-with-return-value.flags
+++ b/src/test/proguard/valid/assume-values-with-return-value.flags
@@ -10,4 +10,6 @@
     public static final int is234To567() return 234..567;
     public static final int isField() return com.google.C.X;
     public static final Object isNull() return null;
+    public static Object returnsNonNull() return _NONNULL_;
+    public static Object returnsNonNullField() return _NONNULL_ com.google.C.X;
 }
\ No newline at end of file