Add support for assumed return of null

This adds support for 'return null' for -assumevalues and
-assumenosideeffects rules.

Bug: 112438627
Change-Id: I6ccc3726c700b2500428b2504b9c67f7ab328875
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index f92eb80..a38e22b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -70,8 +70,8 @@
     Instruction replacement = null;
     ValueType valueType = instruction.outValue().outType();
     if (rule != null && rule.hasReturnValue() && rule.getReturnValue().isSingleValue()) {
-      assert valueType != ValueType.OBJECT;
       Value value = code.createValue(valueType, instruction.getLocalInfo());
+      assert valueType != ValueType.OBJECT || rule.getReturnValue().isNull();
       replacement = new ConstNumber(value, rule.getReturnValue().getSingleValue());
     }
     if (replacement == null &&
@@ -132,12 +132,12 @@
         DexEncodedMethod definition = appInfo
             .lookup(invoke.getType(), invokedMethod, callingContext);
 
-        // Process invokes marked as having no side effects.
         boolean invokeReplaced = false;
         ProguardMemberRuleLookup lookup = lookupMemberRule(definition);
         if (lookup != null) {
           if (lookup.type == RuleType.ASSUME_NO_SIDE_EFFECTS
               && (invoke.outValue() == null || !invoke.outValue().isUsed())) {
+            // Remove invoke if marked as having no side effects and the return value is not used.
             iterator.remove();
             invokeReplaced = true;
           } else if (invoke.outValue() != null && invoke.outValue().isUsed()) {
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 d1a0b3d..740aa2c 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -968,6 +968,8 @@
                     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();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
index b4e9596..a1aad20 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
@@ -8,39 +8,60 @@
 import com.android.tools.r8.utils.LongInterval;
 
 public class ProguardMemberRuleReturnValue {
+  public enum Type {
+    BOOLEAN,
+    VALUE_RANGE,
+    FIELD,
+    NULL
+  }
 
+  private final Type type;
   private final boolean booleanValue;
   private final LongInterval longInterval;
   private final DexField field;
 
   ProguardMemberRuleReturnValue(boolean value) {
+    this.type = Type.BOOLEAN;
     this.booleanValue = value;
     this.longInterval = null;
     this.field = null;
   }
 
   ProguardMemberRuleReturnValue(LongInterval value) {
+    this.type = Type.VALUE_RANGE;
     this.booleanValue = false;
     this.longInterval = value;
     this.field = null;
   }
 
   ProguardMemberRuleReturnValue(DexField field) {
+    this.type = Type.FIELD;
     this.booleanValue = false;
     this.longInterval = null;
     this.field = field;
   }
 
+  ProguardMemberRuleReturnValue() {
+    this.type = Type.NULL;
+    this.booleanValue = false;
+    this.longInterval = null;
+    this.field = null;
+  }
+
   public boolean isBoolean() {
-    return longInterval == null && field == null;
+    return type == Type.BOOLEAN;
   }
 
   public boolean isValueRange() {
-    return longInterval != null && field == null;
+    return type == Type.VALUE_RANGE;
   }
 
   public boolean isField() {
-    return field != null;
+    return type == Type.FIELD;
+  }
+
+  public boolean isNull() {
+    return type == Type.NULL;
   }
 
   public boolean getBoolean() {
@@ -51,22 +72,27 @@
   /**
    * Returns if this return value is a single value.
    *
-   * Boolean values are considered a single value.
+   * Boolean values and null are considered a single value.
    */
   public boolean isSingleValue() {
-    return isBoolean() || (isValueRange() && longInterval.isSingleValue());
+    return isBoolean() || isNull() || (isValueRange() && longInterval.isSingleValue());
   }
 
   /**
    * Returns the return value.
    *
    * Boolean values are returned as 0 for <code>false</code> and 1 for <code>true</code>.
+   *
+   * Reference value <code>null</code> is returned as 0.
    */
   public long getSingleValue() {
     assert isSingleValue();
     if (isBoolean()) {
       return booleanValue ? 1 : 0;
     }
+    if (isNull()) {
+      return 0;
+    }
     return longInterval.getSingleValue();
   }
 
@@ -86,6 +112,8 @@
     result.append(" return ");
     if (isBoolean()) {
       result.append(booleanValue ? "true" : "false");
+    } else if (isNull()) {
+      result.append("null");
     } else if (isValueRange()) {
       result.append(longInterval.getMin());
       if (!isSingleValue()) {
diff --git a/src/test/examples/assumenosideeffects6/Assumenosideeffects.java b/src/test/examples/assumenosideeffects6/Assumenosideeffects.java
new file mode 100644
index 0000000..07630b9
--- /dev/null
+++ b/src/test/examples/assumenosideeffects6/Assumenosideeffects.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2018, 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 assumenosideeffects6;
+
+class A {
+  @CheckDiscarded
+  public static Object methodStaticNull() {
+    System.out.println("methodStaticNull");
+    return new Object();
+  }
+
+  // This method must be on a class which is not explicitly kept (as Assumenosideeffects is).
+  // For an explicitly kept class we cannot expect single target for any methods, so the rule
+  // will never apply (see b/70550443#comment2).
+  @CheckDiscarded
+  public Object methodNull() {
+    System.out.println("methodNull");
+    return new Object();
+  }
+}
+
+public class Assumenosideeffects {
+  public static void main(String[] args) {
+    System.out.println(A.methodStaticNull() != null ? "NOT NULL" : "NULL");
+    System.out.println((new A().methodNull()) != null ? "NOT NULL" : "NULL");
+  }
+}
diff --git a/src/test/examples/assumenosideeffects6/CheckDiscarded.java b/src/test/examples/assumenosideeffects6/CheckDiscarded.java
new file mode 100644
index 0000000..a249a6d
--- /dev/null
+++ b/src/test/examples/assumenosideeffects6/CheckDiscarded.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2018, 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 assumenosideeffects6;
+
+public @interface CheckDiscarded {
+
+}
diff --git a/src/test/examples/assumenosideeffects6/keep-rules-discard.txt b/src/test/examples/assumenosideeffects6/keep-rules-discard.txt
new file mode 100644
index 0000000..0f51575
--- /dev/null
+++ b/src/test/examples/assumenosideeffects6/keep-rules-discard.txt
@@ -0,0 +1,23 @@
+# Copyright (c) 2018, 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects6.Assumenosideeffects {
+  public static void main(...);
+}
+
+# Mark some methods to return null and have no side effects.
+-assumenosideeffects class assumenosideeffects6.A {
+  public static java.lang.Object methodStaticNull() return null;
+  public java.lang.Object methodNull() return null;
+}
+
+# Allow access modification to enable minification.
+-allowaccessmodification
+
+# Check that methods has been discarded.
+-checkdiscard class ** {
+  @assumenosideeffects6.CheckDiscarded *;
+}
diff --git a/src/test/examples/assumenosideeffects6/keep-rules.txt b/src/test/examples/assumenosideeffects6/keep-rules.txt
new file mode 100644
index 0000000..c158dd6
--- /dev/null
+++ b/src/test/examples/assumenosideeffects6/keep-rules.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2018, 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects6.Assumenosideeffects {
+  public static void main(...);
+}
+
+# Mark some methods to return null and have no side effects.
+-assumenosideeffects class assumenosideeffects6.A {
+  public static java.lang.Object methodStaticNull() return null;
+  public java.lang.Object methodNull() return null;
+}
+
+# Allow access modification to enable minification.
+-allowaccessmodification
diff --git a/src/test/examples/assumevalues7/Assumevalues.java b/src/test/examples/assumevalues7/Assumevalues.java
new file mode 100644
index 0000000..e83fc2e
--- /dev/null
+++ b/src/test/examples/assumevalues7/Assumevalues.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2018 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 assumevalues7;
+
+class A {
+  public static Object getObjectStatic() {
+    return new Object();
+  }
+
+  // This method must be on a class which is not explicitly kept (as Assumevalues is).
+  // For an explicitly kept class we cannot expect single target for any methods, so the rule
+  // will never apply (see b/70550443#comment2).
+  public Object getObject() {
+    return new Object();
+  }
+}
+
+public class Assumevalues {
+  public static void main(String[] args) {
+    if (A.getObjectStatic() != null) {
+      System.out.println("NOPE_STATIC_NOT_NULL");
+    }
+    if (new A().getObject() != null) {
+      System.out.println("NOPE_NOT_NULL");
+    }
+    System.out.println("OK");
+  }
+}
diff --git a/src/test/examples/assumevalues7/keep-rules.txt b/src/test/examples/assumevalues7/keep-rules.txt
new file mode 100644
index 0000000..c8c6c2b
--- /dev/null
+++ b/src/test/examples/assumevalues7/keep-rules.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2018, 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep class assumevalues7.Assumevalues {
+  void main(...);
+}
+
+# Mark some methods returning null.
+-assumevalues class assumevalues7.A {
+  public static java.lang.Object getObjectStatic() return null;
+  public java.lang.Object getObject() return null;
+}
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 f79e01f..a690ee8 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -417,12 +417,14 @@
         assertTrue(rule.getReturnValue().isBoolean());
         assertFalse(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
+        assertFalse(rule.getReturnValue().isNull());
         assertEquals(rule.getName().matches("returnsTrue"), rule.getReturnValue().getBoolean());
         matches |= 1 << 0;
       } else if (rule.getName().matches("returns1")) {
         assertFalse(rule.getReturnValue().isBoolean());
         assertTrue(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
+        assertFalse(rule.getReturnValue().isNull());
         assertTrue(rule.getReturnValue().isSingleValue());
         assertEquals(1, rule.getReturnValue().getValueRange().getMin());
         assertEquals(1, rule.getReturnValue().getValueRange().getMax());
@@ -432,6 +434,7 @@
         assertFalse(rule.getReturnValue().isBoolean());
         assertTrue(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
+        assertFalse(rule.getReturnValue().isNull());
         assertFalse(rule.getReturnValue().isSingleValue());
         assertEquals(2, rule.getReturnValue().getValueRange().getMin());
         assertEquals(4, rule.getReturnValue().getValueRange().getMax());
@@ -440,6 +443,7 @@
         assertFalse(rule.getReturnValue().isBoolean());
         assertTrue(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
+        assertFalse(rule.getReturnValue().isNull());
         assertFalse(rule.getReturnValue().isSingleValue());
         assertEquals(234, rule.getReturnValue().getValueRange().getMin());
         assertEquals(567, rule.getReturnValue().getValueRange().getMax());
@@ -448,15 +452,23 @@
         assertFalse(rule.getReturnValue().isBoolean());
         assertFalse(rule.getReturnValue().isValueRange());
         assertTrue(rule.getReturnValue().isField());
+        assertFalse(rule.getReturnValue().isNull());
         assertEquals("com.google.C", rule.getReturnValue().getField().clazz.toString());
         assertEquals("int", rule.getReturnValue().getField().type.toString());
         assertEquals("X", rule.getReturnValue().getField().name.toString());
         matches |= 1 << 4;
+      } else if (rule.getName().matches("returnsNull")) {
+        assertFalse(rule.getReturnValue().isBoolean());
+        assertFalse(rule.getReturnValue().isValueRange());
+        assertFalse(rule.getReturnValue().isField());
+        assertTrue(rule.getReturnValue().isNull());
+        assertTrue(rule.getReturnValue().isSingleValue());
+        matches |= 1 << 5;
       } else {
         fail("Unexpected");
       }
     }
-    assertEquals((1 << 5) - 1, matches);
+    assertEquals((1 << 6) - 1, matches);
   }
 
   @Test
@@ -474,12 +486,14 @@
         assertTrue(rule.getReturnValue().isBoolean());
         assertFalse(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
+        assertFalse(rule.getReturnValue().isNull());
         assertEquals(rule.getName().matches("isTrue"), rule.getReturnValue().getBoolean());
         matches |= 1 << 0;
       } else if (rule.getName().matches("is1")) {
         assertFalse(rule.getReturnValue().isBoolean());
         assertTrue(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
+        assertFalse(rule.getReturnValue().isNull());
         assertTrue(rule.getReturnValue().isSingleValue());
         assertEquals(1, rule.getReturnValue().getValueRange().getMin());
         assertEquals(1, rule.getReturnValue().getValueRange().getMax());
@@ -489,6 +503,7 @@
         assertFalse(rule.getReturnValue().isBoolean());
         assertTrue(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
+        assertFalse(rule.getReturnValue().isNull());
         assertFalse(rule.getReturnValue().isSingleValue());
         assertEquals(2, rule.getReturnValue().getValueRange().getMin());
         assertEquals(4, rule.getReturnValue().getValueRange().getMax());
@@ -497,6 +512,7 @@
         assertFalse(rule.getReturnValue().isBoolean());
         assertTrue(rule.getReturnValue().isValueRange());
         assertFalse(rule.getReturnValue().isField());
+        assertFalse(rule.getReturnValue().isNull());
         assertFalse(rule.getReturnValue().isSingleValue());
         assertEquals(234, rule.getReturnValue().getValueRange().getMin());
         assertEquals(567, rule.getReturnValue().getValueRange().getMax());
@@ -505,15 +521,23 @@
         assertFalse(rule.getReturnValue().isBoolean());
         assertFalse(rule.getReturnValue().isValueRange());
         assertTrue(rule.getReturnValue().isField());
+        assertFalse(rule.getReturnValue().isNull());
         assertEquals("com.google.C", rule.getReturnValue().getField().clazz.toString());
         assertEquals("int", rule.getReturnValue().getField().type.toString());
         assertEquals("X", rule.getReturnValue().getField().name.toString());
         matches |= 1 << 4;
+      } else if (rule.getName().matches("isNull")) {
+        assertFalse(rule.getReturnValue().isBoolean());
+        assertFalse(rule.getReturnValue().isValueRange());
+        assertFalse(rule.getReturnValue().isField());
+        assertTrue(rule.getReturnValue().isNull());
+        assertTrue(rule.getReturnValue().isSingleValue());
+        matches |= 1 << 5;
       } else {
         fail("Unexpected");
       }
     }
-    assertEquals((1 << 5) - 1, matches);
+    assertEquals((1 << 6) - 1, matches);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumenosideeffects6Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumenosideeffects6Test.java
new file mode 100644
index 0000000..578ad2a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumenosideeffects6Test.java
@@ -0,0 +1,66 @@
+// Copyright (c) 2018, 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.shaking.examples;
+
+import com.android.tools.r8.TestBase.MinifyMode;
+import com.android.tools.r8.shaking.TreeShakingTest;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TreeShakingAssumenosideeffects6Test extends TreeShakingTest {
+
+  @Parameters(name = "mode:{0}-{1} minify:{2}")
+  public static Collection<Object[]> data() {
+    List<Object[]> parameters = new ArrayList<>();
+    for (MinifyMode minify : MinifyMode.values()) {
+      parameters.add(new Object[] {Frontend.JAR, Backend.CF, minify});
+      parameters.add(new Object[] {Frontend.JAR, Backend.DEX, minify});
+      parameters.add(new Object[] {Frontend.DEX, Backend.DEX, minify});
+    }
+    return parameters;
+  }
+
+  public TreeShakingAssumenosideeffects6Test(
+      Frontend frontend, Backend backend, MinifyMode minify) {
+    super(
+        "examples/assumenosideeffects6",
+        "assumenosideeffects6.Assumenosideeffects",
+        frontend,
+        backend,
+        minify);
+  }
+
+  @Test
+  public void testKeeprules() throws Exception {
+    runTest(
+        null,
+        TreeShakingAssumenosideeffects6Test::assumenosideeffects6CheckOutput,
+        null,
+        ImmutableList.of("src/test/examples/assumenosideeffects6/keep-rules.txt"));
+  }
+
+  @Test
+  public void testKeeprulesdiscard() throws Exception {
+    runTest(
+        null,
+        TreeShakingAssumenosideeffects6Test::assumenosideeffects6CheckOutput,
+        null,
+        ImmutableList.of("src/test/examples/assumenosideeffects6/keep-rules-discard.txt"));
+  }
+
+  private static void assumenosideeffects6CheckOutput(String output1, String output2) {
+    Assert.assertEquals(
+        StringUtils.lines("methodStaticNull", "NOT NULL", "methodNull", "NOT NULL"), output1);
+    Assert.assertEquals(StringUtils.lines("NULL", "NULL"), output2);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumevalues7Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumevalues7Test.java
new file mode 100644
index 0000000..789e40c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumevalues7Test.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2018, 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.shaking.examples;
+
+import com.android.tools.r8.TestBase.MinifyMode;
+import com.android.tools.r8.shaking.TreeShakingTest;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.ConstStringInstructionSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TreeShakingAssumevalues7Test extends TreeShakingTest {
+
+  @Parameters(name = "mode:{0}-{1} minify:{2}")
+  public static Collection<Object[]> data() {
+    List<Object[]> parameters = new ArrayList<>();
+    for (MinifyMode minify : MinifyMode.values()) {
+      parameters.add(new Object[] {Frontend.JAR, Backend.CF, minify});
+      parameters.add(new Object[] {Frontend.JAR, Backend.DEX, minify});
+      parameters.add(new Object[] {Frontend.DEX, Backend.DEX, minify});
+    }
+    return parameters;
+  }
+
+  public TreeShakingAssumevalues7Test(Frontend frontend, Backend backend, MinifyMode minify) {
+    super("examples/assumevalues7", "assumevalues7.Assumevalues", frontend, backend, minify);
+  }
+
+  @Test
+  public void test() throws Exception {
+    runTest(
+        getBackend() == Backend.DEX ? TreeShakingAssumevalues7Test::assumevalues7CheckCode : null,
+        TreeShakingAssumevalues7Test::assumevalues7CheckOutput,
+        null,
+        ImmutableList.of("src/test/examples/assumevalues7/keep-rules.txt"));
+  }
+
+  private static void assumevalues7CheckCode(CodeInspector inspector) {
+    inspector.forAllClasses(c -> {
+      c.forAllMethods(m -> {
+        if (m.getFinalName().equals("main")) {
+          m.iterateInstructions().forEachRemaining(i -> {
+            if (i.isConstString(JumboStringMode.ALLOW)) {
+              ConstStringInstructionSubject str = (ConstStringInstructionSubject) i;
+              assert !str.getString().toASCIIString().contains("NOPE");
+            }
+          });
+        }
+      });
+    });
+  }
+
+  private static void assumevalues7CheckOutput(String output1, String output2) {
+    Assert.assertEquals(StringUtils.lines("NOPE_STATIC_NOT_NULL", "NOPE_NOT_NULL", "OK"), output1);
+    Assert.assertEquals(StringUtils.lines("OK"), output2);
+  }
+}
\ No newline at end of file
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 78d3683..b233ea3 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
@@ -9,4 +9,5 @@
     public static int returns2To4() return 2..4;
     public static int returns234To567() return 234..567;
     public static int returnsField() return com.google.C.X;
+    public static Object returnsNull() return null;
 }
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 7f89a79..90cefbe 100644
--- a/src/test/proguard/valid/assume-values-with-return-value.flags
+++ b/src/test/proguard/valid/assume-values-with-return-value.flags
@@ -9,4 +9,5 @@
     public static final int is2To4() return 2..4;
     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;
 }
\ No newline at end of file