[Metadata] Keep track of companion object name in KotlinClassInfo

Bug: b/248450861
Change-Id: Icbe99516fa323f427f7b6926b63a8385698b5e2f
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
index 9ddddfe..100d61a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -54,6 +54,7 @@
   private final String inlineClassUnderlyingPropertyName;
   private final KotlinTypeInfo inlineClassUnderlyingType;
   private final int jvmFlags;
+  private final String companionObjectName;
 
   // List of tracked assignments of kotlin metadata.
   private final KotlinMetadataMembersTracker originalMembersWithKotlinInfo;
@@ -78,7 +79,8 @@
       String inlineClassUnderlyingPropertyName,
       KotlinTypeInfo inlineClassUnderlyingType,
       KotlinMetadataMembersTracker originalMembersWithKotlinInfo,
-      int jvmFlags) {
+      int jvmFlags,
+      String companionObjectName) {
     this.flags = flags;
     this.name = name;
     this.nameCanBeSynthesizedFromClassOrAnonymousObjectOrigin =
@@ -100,6 +102,7 @@
     this.inlineClassUnderlyingType = inlineClassUnderlyingType;
     this.originalMembersWithKotlinInfo = originalMembersWithKotlinInfo;
     this.jvmFlags = jvmFlags;
+    this.companionObjectName = companionObjectName;
   }
 
   public static KotlinClassInfo create(
@@ -154,7 +157,7 @@
             keepByteCode,
             extensionInformation,
             originalMembersWithKotlinInfo);
-    setCompanionObject(kmClass, hostClass, reporter);
+    String companionObjectName = setCompanionObject(kmClass, hostClass, reporter);
     KotlinTypeReference anonymousObjectOrigin = getAnonymousObjectOrigin(kmClass, factory);
     boolean nameCanBeDeducedFromClassOrOrigin =
         kmClass.name.equals(
@@ -183,7 +186,8 @@
         kmClass.getInlineClassUnderlyingPropertyName(),
         KotlinTypeInfo.create(kmClass.getInlineClassUnderlyingType(), factory, reporter),
         originalMembersWithKotlinInfo,
-        JvmExtensionsKt.getJvmFlags(kmClass));
+        JvmExtensionsKt.getJvmFlags(kmClass),
+        companionObjectName);
   }
 
   private static KotlinTypeReference getAnonymousObjectOrigin(
@@ -228,19 +232,20 @@
     return superTypeInfos.build();
   }
 
-  private static void setCompanionObject(KmClass kmClass, DexClass hostClass, Reporter reporter) {
+  private static String setCompanionObject(KmClass kmClass, DexClass hostClass, Reporter reporter) {
     String companionObjectName = kmClass.getCompanionObject();
     if (companionObjectName == null) {
-      return;
+      return companionObjectName;
     }
     for (DexEncodedField field : hostClass.fields()) {
       if (field.getReference().name.toString().equals(companionObjectName)) {
         field.setKotlinMemberInfo(new KotlinCompanionInfo(companionObjectName));
-        return;
+        return companionObjectName;
       }
     }
     reporter.warning(
         KotlinMetadataDiagnostic.missingCompanionObject(hostClass, companionObjectName));
+    return companionObjectName;
   }
 
   @Override
@@ -284,6 +289,7 @@
       rewritten |= !name.equals(rewrittenName);
     }
     // Find a companion object.
+    boolean foundCompanion = false;
     for (DexEncodedField field : clazz.fields()) {
       if (field.getKotlinInfo().isCompanion()) {
         rewritten |=
@@ -291,8 +297,14 @@
                 .getKotlinInfo()
                 .asCompanion()
                 .rewrite(kmClass, field.getReference(), appView.getNamingLens());
+        foundCompanion = true;
       }
     }
+    // If we did not find a companion but it was there on input we have to emit a new metadata
+    // object.
+    if (!foundCompanion && companionObjectName != null) {
+      rewritten = true;
+    }
     // Take all not backed constructors because we will never find them in definitions.
     for (KotlinConstructorInfo constructorInfo : constructorsWithNoBacking) {
       rewritten |= constructorInfo.rewrite(kmClass, null, appView);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteRemovedCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteRemovedCompanionTest.java
index 2f27bf0..e4f94f6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteRemovedCompanionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteRemovedCompanionTest.java
@@ -4,10 +4,7 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertThrows;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.StringUtils;
@@ -89,23 +86,29 @@
   }
 
   @Test
-  public void testMetadataInCompanion_removedField() {
-    // TODO(b/248450861): Should not throw.
-    assertThrows(
-        CompilationFailedException.class,
-        () ->
-            testForR8(parameters.getBackend())
-                .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
-                .addProgramFiles(companionRemoveJarMap.getForConfiguration(kotlinc, targetVersion))
-                // Keep the B class and its interface (which has the doStuff method).
-                .addKeepKotlinMetadata()
-                .addKeepRules(
-                    "-keep class **.ClassWithCompanion { void <init>(); void doStuff(); }")
-                .addKeepRules("-keep class **.ClassWithCompanion$Companion")
-                .compileWithExpectedDiagnostics(
-                    diagnostics -> {
-                      diagnostics.assertErrorMessageThatMatches(
-                          containsString("The metadata should be equivalent"));
-                    }));
+  public void testMetadataInCompanion_removedField() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
+            .addProgramFiles(companionRemoveJarMap.getForConfiguration(kotlinc, targetVersion))
+            // Keep the ClasWithCompanion class.
+            .addKeepKotlinMetadata()
+            .addKeepRules("-keep class **.ClassWithCompanion { void <init>(); void doStuff(); }")
+            .addKeepRules("-keep class **.ClassWithCompanion$Companion")
+            .compile()
+            .writeToZip();
+
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/companion_remove_app", "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG + ".companion_remove_app.MainKt")
+        .assertSuccessWithOutput(EXPECTED);
   }
 }