Fix clobbering in mapping when recording sourcefile for pruned classes

Bug: b/279702361
Change-Id: Ia2895fa2597ba5264ee420c2a09a5eacc37f7509
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index b9bc6bc..c568a31 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -76,6 +76,10 @@
       }
     }
 
+    public boolean hasMapping(String obfuscatedName) {
+      return mapping.containsKey(obfuscatedName);
+    }
+
     @Override
     public ClassNameMapper build() {
       return new ClassNameMapper(
diff --git a/src/main/java/com/android/tools/r8/naming/SeedMapper.java b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
index 45f7a5b..c94b593 100644
--- a/src/main/java/com/android/tools/r8/naming/SeedMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.utils.DescriptorUtils.descriptorToInternalName;
 import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
 import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor;
+import static com.android.tools.r8.utils.positions.MappedPositionToClassNameMapperBuilder.getPrunedInlinedClassObfuscatedPrefix;
 
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.MemberNaming.Signature;
@@ -56,8 +57,11 @@
       ClassNamingForMapApplier.Builder classNamingBuilder =
           ClassNamingForMapApplier.builder(
               renamedDescriptorName, originalDescriptor, position, reporter);
-      if (map.put(originalDescriptor, classNamingBuilder) != null) {
-        reporter.error(ProguardMapError.duplicateSourceClass(originalName, position));
+      // Disallow renaming to a synthetic chosen name for pruned classes.
+      if (!renamedName.startsWith(getPrunedInlinedClassObfuscatedPrefix())) {
+        if (map.put(originalDescriptor, classNamingBuilder) != null) {
+          reporter.error(ProguardMapError.duplicateSourceClass(originalDescriptor, position));
+        }
       }
       return classNamingBuilder;
     }
diff --git a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
index ae0eb0e..d2d30bd 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
@@ -43,6 +43,7 @@
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.synthesis.SyntheticItems;
 import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.IntBox;
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.OneShotCollectionConsumer;
@@ -63,6 +64,7 @@
 public class MappedPositionToClassNameMapperBuilder {
 
   private static final int MAX_LINE_NUMBER = 65535;
+  private static final String PRUNED_INLINED_CLASS_OBFUSCATED_PREFIX = "R8$$REMOVED$$CLASS$$";
 
   private final OriginalSourceFiles originalSourceFiles;
   private final AppView<?> appView;
@@ -85,6 +87,10 @@
         appView.options().getMapFileVersion().toMapVersionMappingInformation());
   }
 
+  public static String getPrunedInlinedClassObfuscatedPrefix() {
+    return PRUNED_INLINED_CLASS_OBFUSCATED_PREFIX;
+  }
+
   public static int getMaxLineNumber() {
     return MAX_LINE_NUMBER;
   }
@@ -104,16 +110,24 @@
   private void addSourceFileLinesForPrunedClasses() {
     // Add all pruned inline classes to the mapping to recover source files.
     List<Entry<DexType, String>> prunedEntries = new ArrayList<>(prunedInlinedClasses.entrySet());
+    IntBox counter = new IntBox();
     prunedEntries.sort(Entry.comparingByKey());
     prunedEntries.forEach(
         entry -> {
           DexType holder = entry.getKey();
-          assert appView.appInfo().definitionForWithoutExistenceAssert(holder) == null;
+          assert appView.appInfo().definitionForWithoutExistenceAssert(holder) == null
+              || !appView.appInfo().definitionForWithoutExistenceAssert(holder).isProgramClass();
           String typeName = holder.toSourceString();
           String sourceFile = entry.getValue();
+          // We have to pick a right-hand side destination that does not overlap with an existing
+          // mapping.
+          String renamedName;
+          do {
+            renamedName = PRUNED_INLINED_CLASS_OBFUSCATED_PREFIX + counter.getAndIncrement();
+          } while (classNameMapperBuilder.hasMapping(renamedName));
           classNameMapperBuilder
               .classNamingBuilder(
-                  typeName, typeName, com.android.tools.r8.position.Position.UNKNOWN)
+                  renamedName, typeName, com.android.tools.r8.position.Position.UNKNOWN)
               .addMappingInformation(FileNameInformation.build(sourceFile), Unreachable::raise);
         });
   }
diff --git a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
index fa9de18..5b85343 100644
--- a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
@@ -139,7 +139,7 @@
   private static void verifyUniqueNaming(CodeInspector inspector, List<String> klasses) {
     Set<String> renamedNames = Sets.newHashSet();
     for (String klass : klasses) {
-      String finalName = inspector.clazz(klass).getFinalName();
+      String finalName = inspector.getObfuscatedTypeName(klass);
       assertFalse(renamedNames.contains(finalName));
       renamedNames.add(finalName);
     }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index b569a57..814a684 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -336,15 +336,17 @@
     String name = DescriptorUtils.descriptorToJavaType(descriptor);
     ClassNamingForNameMapper naming = null;
     if (mapping != null) {
-      String obfuscated = originalToObfuscatedMapping.get(name);
-      if (obfuscated != null) {
-        naming = mapping.getClassNaming(obfuscated);
-        name = obfuscated;
+      // Figure out if the name is an already obfuscated name. It is important to look up by
+      // residual name first to ensure that we do not accidentally find a class that has been
+      // pruned.
+      String original = obfuscatedToOriginalMapping.get(name);
+      if (original != null) {
+        naming = mapping.getClassNaming(name);
       } else {
-        // Figure out if the name is an already obfuscated name.
-        String original = obfuscatedToOriginalMapping.get(name);
-        if (original != null) {
-          naming = mapping.getClassNaming(name);
+        String obfuscated = originalToObfuscatedMapping.get(name);
+        if (obfuscated != null) {
+          naming = mapping.getClassNaming(obfuscated);
+          name = obfuscated;
         }
       }
     }
@@ -400,7 +402,7 @@
     return clazz.method(method);
   }
 
-  String getObfuscatedTypeName(String originalTypeName) {
+  public String getObfuscatedTypeName(String originalTypeName) {
     String obfuscatedTypeName = null;
     if (mapping != null) {
       obfuscatedTypeName = mapType(originalToObfuscatedMapping, originalTypeName);
@@ -408,7 +410,7 @@
     return obfuscatedTypeName != null ? obfuscatedTypeName : originalTypeName;
   }
 
-  String getOriginalTypeName(String minifiedTypeName) {
+  public String getOriginalTypeName(String minifiedTypeName) {
     String originalTypeName = null;
     if (mapping != null) {
       originalTypeName = mapType(obfuscatedToOriginalMapping, minifiedTypeName);