Add test for repackaging collision with missing class

Bug: 196406764
Change-Id: Ia16d9ec7a770eaf5d8fd9bfc21eef5cbe362b0e0
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 88d0852..ded2d2c 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -623,7 +623,11 @@
           continue;
         }
         DexMethod originalMethod = getOriginalMethodSignature(method.getReference());
-        assert originalMethods.contains(originalMethod);
+        assert originalMethods.contains(originalMethod)
+            : "Method could not be mapped back: "
+                + method.toSourceString()
+                + ", originalMethod: "
+                + originalMethod.toSourceString();
       }
     }
 
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageMissingTypeCollisionTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageMissingTypeCollisionTest.java
new file mode 100644
index 0000000..4dc7f1f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageMissingTypeCollisionTest.java
@@ -0,0 +1,143 @@
+// Copyright (c) 2021, 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.repackage;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.R8CompatTestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/* This is a reproduction of b/196406764 where a collision will appear when repackaging */
+@RunWith(Parameterized.class)
+public class RepackageMissingTypeCollisionTest extends RepackageTestBase {
+
+  public RepackageMissingTypeCollisionTest(
+      String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+    super(flattenPackageHierarchyOrRepackageClasses, parameters);
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    String newMissingTypeName =
+        getRepackagePackage() + (isFlattenPackageHierarchy() ? ".a.a" : ".a");
+    String newMissingDescriptor = DescriptorUtils.javaTypeToDescriptor(newMissingTypeName);
+    String newATypeName = A.class.getPackage().getName() + ".a";
+    String newADescriptor = DescriptorUtils.javaTypeToDescriptor(newATypeName);
+    testForJvm()
+        .addProgramClassFileData(
+            transformer(A.class).setClassDescriptor(newADescriptor).transform(),
+            transformer(Anno.class)
+                .replaceClassDescriptorInMembers(descriptor(Missing.class), newMissingDescriptor)
+                .replaceClassDescriptorInAnnotationDefault(
+                    descriptor(Missing.class), newMissingDescriptor)
+                .transform(),
+            transformer(Main.class)
+                .replaceClassDescriptorInMethodInstructions(descriptor(A.class), newADescriptor)
+                .replaceClassDescriptorInMethodInstructions(
+                    descriptor(Missing.class), newMissingDescriptor)
+                .transform())
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
+  }
+
+  @Test
+  public void testRepackageMissingCollision() throws Exception {
+    testMissingReference(true)
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject clazz = inspector.clazz(getNewATypeName());
+              assertThat(clazz, isPresentAndRenamed());
+              assertEquals(getNewMissingTypeName(), clazz.getFinalName());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatMatches(not(containsString("NoClassDefFoundError")));
+  }
+
+  @Test
+  public void testNoRepackage() throws Exception {
+    testMissingReference(false)
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
+  }
+
+  private String getNewMissingTypeName() {
+    return getRepackagePackage() + (isFlattenPackageHierarchy() ? ".a.a" : ".a");
+  }
+
+  private String getNewATypeName() {
+    return A.class.getPackage().getName() + ".a";
+  }
+
+  private R8CompatTestBuilder testMissingReference(boolean repackage) throws Exception {
+    // The references to Missing will be rewritten to <repackage>.a but the definition will not be
+    // present.
+    String newMissingDescriptor = DescriptorUtils.javaTypeToDescriptor(getNewMissingTypeName());
+    String newADescriptor = DescriptorUtils.javaTypeToDescriptor(getNewATypeName());
+    return testForR8Compat(parameters.getBackend())
+        .addProgramClassFileData(
+            transformer(A.class).setClassDescriptor(newADescriptor).transform(),
+            transformer(Anno.class)
+                .replaceClassDescriptorInMembers(descriptor(Missing.class), newMissingDescriptor)
+                .replaceClassDescriptorInAnnotationDefault(
+                    descriptor(Missing.class), newMissingDescriptor)
+                .transform(),
+            transformer(Main.class)
+                .replaceClassDescriptorInMethodInstructions(descriptor(A.class), newADescriptor)
+                .replaceClassDescriptorInMethodInstructions(
+                    descriptor(Missing.class), newMissingDescriptor)
+                .transform())
+        .addKeepMainRule(Main.class)
+        .addKeepClassAndMembersRules(Anno.class)
+        .addKeepRuntimeVisibleAnnotations()
+        .applyIf(repackage, this::configureRepackaging)
+        .setMinApi(parameters.getApiLevel())
+        .addDontWarn(getNewMissingTypeName())
+        .addOptionsModification(internalOptions -> internalOptions.enableEnumUnboxing = false)
+        .addHorizontallyMergedClassesInspector(
+            HorizontallyMergedClassesInspector::assertNoClassesMerged);
+  }
+
+  /* Will be missing on input */
+  public enum Missing {
+    foo;
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @Target(ElementType.TYPE)
+  public @interface Anno {
+
+    /* Renamed to <repackage>.a */ Missing missing() default Missing.foo;
+  }
+
+  @Anno
+  public enum /* renamed to a */ A {
+    foo;
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      Anno annotation = A.class.getAnnotation(Anno.class);
+      System.out.println(annotation.missing());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 61b3343..1b99096 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromDescriptor;
 import static com.android.tools.r8.utils.StringUtils.replaceAll;
 import static org.objectweb.asm.Opcodes.ASM7;
+import static org.objectweb.asm.Opcodes.ASM9;
 
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
@@ -763,6 +764,29 @@
         });
   }
 
+  public ClassFileTransformer replaceClassDescriptorInAnnotationDefault(
+      String oldDescriptor, String newDescriptor) {
+    return addMethodTransformer(
+        new MethodTransformer() {
+
+          @Override
+          public AnnotationVisitor visitAnnotationDefault() {
+            return new AnnotationVisitor(ASM9, super.visitAnnotationDefault()) {
+              @Override
+              public void visit(String name, Object value) {
+                super.visit(name, value);
+              }
+
+              @Override
+              public void visitEnum(String name, String descriptor, String value) {
+                super.visitEnum(
+                    name, descriptor.equals(oldDescriptor) ? newDescriptor : descriptor, value);
+              }
+            };
+          }
+        });
+  }
+
   public ClassFileTransformer replaceClassDescriptorInMethodInstructions(
       String oldDescriptor, String newDescriptor) {
     return addMethodTransformer(