Report info diagnostics for duplicates in program and library

Bug: b/120884788
Change-Id: I5ab1e3c6f3ca90619725f63de88bb738e1762b5c
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index bbaa2b9..9c1ab31 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -265,6 +265,7 @@
       System.out.println("R8 is running with free memory:" + runtime.freeMemory());
       System.out.println("R8 is running with max memory:" + runtime.maxMemory());
     }
+    options.prepareForReportingLibraryAndProgramDuplicates();
     try {
       AppView<AppInfoWithClassHierarchy> appView;
       {
@@ -437,6 +438,8 @@
       assert appView.appInfo().hasLiveness();
       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
 
+      options.reportLibraryAndProgramDuplicates(appViewWithLiveness);
+
       new CfOpenClosedInterfacesAnalysis(appViewWithLiveness).run(executorService);
 
       assert verifyNoJarApplicationReaders(appView.appInfo().classes());
diff --git a/src/main/java/com/android/tools/r8/errors/DuplicateTypeInProgramAndLibraryDiagnostic.java b/src/main/java/com/android/tools/r8/errors/DuplicateTypeInProgramAndLibraryDiagnostic.java
new file mode 100644
index 0000000..5af3923
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/DuplicateTypeInProgramAndLibraryDiagnostic.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2023, 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.errors;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.ClassReference;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
+@Keep
+public class DuplicateTypeInProgramAndLibraryDiagnostic extends DuplicateTypesDiagnostic {
+
+  public DuplicateTypeInProgramAndLibraryDiagnostic(
+      ClassReference type, Origin programOrigin, Origin libraryOrigin) {
+    super(type, ImmutableList.of(programOrigin, libraryOrigin));
+  }
+
+  /** Get the origin of the program definition for the duplicated type. */
+  public Origin getProgramOrigin() {
+    return ((List<Origin>) getOrigins()).get(0);
+  }
+
+  /** Get the origin of the library definition for the duplicated type. */
+  public Origin getLibraryOrigin() {
+    return ((List<Origin>) getOrigins()).get(1);
+  }
+
+  @Override
+  public String getDiagnosticMessage() {
+    String typeName = getType().getTypeName();
+    return "Type "
+        + typeName
+        + " is defined by both the program: "
+        + getProgramOrigin()
+        + " and the library: "
+        + getLibraryOrigin();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
index b5b28fd..8fedcdd 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -202,7 +202,12 @@
                   allLibraryClasses,
                   type -> {
                     DexProgramClass clazz = programClasses.get(type);
-                    return clazz != null ? clazz : classpathClasses.get(type);
+                    if (clazz != null) {
+                      options.recordLibraryAndProgramDuplicate(
+                          type, clazz, allLibraryClasses.get(type));
+                      return clazz;
+                    }
+                    return classpathClasses.get(type);
                   },
                   options);
         }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index ea3b56f..017b6a0 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -34,6 +34,7 @@
 import com.android.tools.r8.dex.VirtualFile;
 import com.android.tools.r8.dump.DumpOptions;
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.DuplicateTypeInProgramAndLibraryDiagnostic;
 import com.android.tools.r8.errors.IncompleteNestNestDesugarDiagnosic;
 import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
 import com.android.tools.r8.errors.InvalidDebugInfoException;
@@ -123,6 +124,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -882,8 +884,6 @@
     return testing.readInputStackMaps ? testing.readInputStackMaps : isGeneratingClassFiles();
   }
 
-  public boolean printCfg = false;
-  public String printCfgFile;
   public boolean ignoreMissingClasses = false;
   public boolean reportMissingClassesInEnclosingMethodAttribute = false;
   public boolean reportMissingClassesInInnerClassAttributes = false;
@@ -1069,6 +1069,46 @@
   private final Map<Origin, List<Pair<ProgramMethod, String>>> warningInvalidDebugInfo =
       new HashMap<>();
 
+  private Map<DexType, Pair<DexProgramClass, DexLibraryClass>> warningLibraryProgramDuplicates;
+
+  public void recordLibraryAndProgramDuplicate(
+      DexType type, DexProgramClass programClass, DexLibraryClass libraryClass) {
+    if (warningLibraryProgramDuplicates != null) {
+      warningLibraryProgramDuplicates.computeIfAbsent(
+          type, k -> new Pair<>(programClass, libraryClass));
+    }
+  }
+
+  public void prepareForReportingLibraryAndProgramDuplicates() {
+    warningLibraryProgramDuplicates = new ConcurrentHashMap<>();
+  }
+
+  public void reportLibraryAndProgramDuplicates(AppView<AppInfoWithLiveness> appViewWithLiveness) {
+    assert warningLibraryProgramDuplicates != null;
+    if (warningLibraryProgramDuplicates.isEmpty()) {
+      warningLibraryProgramDuplicates = null;
+      return;
+    }
+    List<DexType> sortedKeys =
+        ListUtils.sort(warningLibraryProgramDuplicates.keySet(), DexType::compareTo);
+    for (DexType key : sortedKeys) {
+      // If the type has been pruned from the program then don't issue a diagnostic.
+      if (DexProgramClass.asProgramClassOrNull(
+              appViewWithLiveness.appInfo().definitionForWithoutExistenceAssert(key))
+          == null) {
+        continue;
+      }
+      Pair<DexProgramClass, DexLibraryClass> classes = warningLibraryProgramDuplicates.get(key);
+      reporter.info(
+          new DuplicateTypeInProgramAndLibraryDiagnostic(
+              key.asClassReference(),
+              classes.getFirst().getOrigin(),
+              classes.getSecond().getOrigin()));
+    }
+    warningLibraryProgramDuplicates = null;
+    reporter.failIfPendingErrors();
+  }
+
   // Don't read code from dex files. Used to extract non-code information from vdex files where
   // the code contains unsupported byte codes.
   public boolean skipReadingDexCode = false;
diff --git a/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java b/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java
index c58b9e1..f7f771f 100644
--- a/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java
+++ b/src/test/java/com/android/tools/r8/classlookup/LibraryClassExtendsProgramClassTest.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.classlookup;
 
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
@@ -13,9 +14,12 @@
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompileResult;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.DuplicateTypeInProgramAndLibraryDiagnostic;
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -163,7 +167,9 @@
     try {
       builder.compileWithExpectedDiagnostics(
           diagnostics -> {
-            diagnostics.assertOnlyErrors();
+            diagnostics.assertNoWarnings();
+            diagnostics.assertAllInfosMatch(
+                diagnosticType(DuplicateTypeInProgramAndLibraryDiagnostic.class));
             checkDiagnostics(diagnostics.getErrors());
           });
       fail("Expected compilation failure");
@@ -180,11 +186,14 @@
         .addProgramClassFileData(junitClasses)
         .addKeepAllClassesRule()
         .addOptionsModification(options -> options.lookupLibraryBeforeProgram = false)
-        .allowDiagnosticWarningMessages(libraryContainsJUnit())
+        .applyIf(libraryContainsJUnit(), R8TestBuilder::allowDiagnosticMessages)
         .compile()
         .inspectDiagnosticMessages(
             diagnostics -> {
               if (libraryContainsJUnit()) {
+                diagnostics.assertNoErrors();
+                diagnostics.assertAllInfosMatch(
+                    diagnosticType(DuplicateTypeInProgramAndLibraryDiagnostic.class));
                 checkDiagnostics(diagnostics.getWarnings());
               }
             })
@@ -200,8 +209,12 @@
         .addKeepAllClassesRule()
         .applyIf(libraryContainsJUnit(), builder -> builder.addDontWarn("android.test.**"))
         .addOptionsModification(options -> options.lookupLibraryBeforeProgram = false)
+        .allowDiagnosticInfoMessages(libraryContainsJUnit())
         .compile()
-        .assertNoMessages();
+        .applyIf(
+            libraryContainsJUnit(),
+            TestCompileResult::assertOnlyInfos,
+            TestCompileResult::assertNoMessages);
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaContextDuplicateInLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaContextDuplicateInLibraryTest.java
index c343ea8..5d5b969 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaContextDuplicateInLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaContextDuplicateInLibraryTest.java
@@ -3,11 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.desugar;
 
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.DuplicateTypeInProgramAndLibraryDiagnostic;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.StringUtils;
@@ -89,7 +91,11 @@
                 b.getBuilder()
                     .setDexClassChecksumFilter(
                         (desc, checksum) -> !desc.contains(binaryName(LIBRARY_CONTEXT))))
-        .compile()
+        .allowDiagnosticInfoMessages()
+        .compileWithExpectedDiagnostics(
+            diagnostics ->
+                diagnostics.assertAllInfosMatch(
+                    diagnosticType(DuplicateTypeInProgramAndLibraryDiagnostic.class)))
         .addRunClasspathClasses(LIBRARY)
         .run(parameters.getRuntime(), MAIN)
         .applyIf(
diff --git a/src/test/java/com/android/tools/r8/diagnostics/ProgramAndLibraryDuplicatesTest.java b/src/test/java/com/android/tools/r8/diagnostics/ProgramAndLibraryDuplicatesTest.java
new file mode 100644
index 0000000..e9b75b6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/diagnostics/ProgramAndLibraryDuplicatesTest.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2023, 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.diagnostics;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.DuplicateTypeInProgramAndLibraryDiagnostic;
+import com.android.tools.r8.errors.DuplicateTypesDiagnostic;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ProgramAndLibraryDuplicatesTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withDefaultRuntimes()
+        .withMinimumApiLevel()
+        .enableApiLevelsForCf()
+        .build();
+  }
+
+  public ProgramAndLibraryDuplicatesTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    testForD8(parameters.getBackend())
+        .addInnerClasses(ProgramAndLibraryDuplicatesTest.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .addLibraryClasses(MyFunction.class)
+        .setMinApi(parameters)
+        .compile()
+        .assertNoMessages()
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(ProgramAndLibraryDuplicatesTest.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .addLibraryClasses(MyFunction.class)
+        .setMinApi(parameters)
+        .addKeepMainRule(TestClass.class)
+        .allowDiagnosticInfoMessages()
+        .compileWithExpectedDiagnostics(
+            diagnostics ->
+                diagnostics
+                    .assertOnlyInfos()
+                    .assertInfosMatch(
+                        allOf(
+                            diagnosticType(DuplicateTypesDiagnostic.class),
+                            diagnosticType(DuplicateTypeInProgramAndLibraryDiagnostic.class),
+                            diagnosticMessage(containsString(typeName(MyFunction.class))))))
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  interface MyFunction<T, R> {
+    R apply(T arg);
+  }
+
+  static class TestClass {
+
+    public static void run(MyFunction<String, String> fn) {
+      System.out.println(fn.apply("Hello"));
+    }
+
+    public static void main(String[] args) {
+      run(prefix -> prefix + ", world");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/logging/AndroidLogRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/logging/AndroidLogRemovalTest.java
index 79a7e19..2fafa0f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/logging/AndroidLogRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/logging/AndroidLogRemovalTest.java
@@ -4,10 +4,12 @@
 
 package com.android.tools.r8.ir.optimize.logging;
 
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.errors.DuplicateTypeInProgramAndLibraryDiagnostic;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
@@ -79,7 +81,16 @@
                 transformer(Log.class).setClassDescriptor("Landroid/util/Log;").transform())
             .addKeepAllClassesRule()
             .setMinApi(parameters)
-            .compile()
+            .allowDiagnosticInfoMessages(parameters.isDexRuntime())
+            .compileWithExpectedDiagnostics(
+                diagnostics -> {
+                  if (parameters.isDexRuntime()) {
+                    diagnostics.assertAllInfosMatch(
+                        diagnosticType(DuplicateTypeInProgramAndLibraryDiagnostic.class));
+                  } else {
+                    diagnostics.assertNoMessages();
+                  }
+                })
             .writeToZip();
 
     testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java b/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
index f7a8f50..d4b9204 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
@@ -3,33 +3,38 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.maindexlist.b72312389;
 
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
 
 import com.android.tools.r8.BaseCommand;
-import com.android.tools.r8.CompatProguardCommandBuilder;
 import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.Diagnostic;
-import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.GenerateMainDexList;
 import com.android.tools.r8.GenerateMainDexListCommand;
-import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.errors.DuplicateTypeInProgramAndLibraryDiagnostic;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Paths;
-import java.util.ArrayList;
 import java.util.List;
 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 B72312389 extends TestBase {
+
   // TODO(120884788): Remove this when default is true.
   private static boolean lookupLibraryBeforeProgram = false;
 
@@ -53,10 +58,19 @@
       "  *;",
       "}");
 
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return TestParameters.builder().withNoneRuntime().build();
+  }
+
+  public B72312389(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
   @Test
   public void testGenerateMainDexList() throws Exception {
     assumeFalse(ToolHelper.isWindows());
-    CollectingDiagnosticHandler diagnostics = new CollectingDiagnosticHandler();
+    TestDiagnosticMessagesImpl diagnostics = new TestDiagnosticMessagesImpl();
     GenerateMainDexListCommand.Builder builder = GenerateMainDexListCommand.builder(diagnostics);
     buildInstrumentationTestCaseApplication(builder);
     GenerateMainDexListCommand command = builder
@@ -68,7 +82,7 @@
     } else {
       assertTrue(mainDexList.contains("junit/framework/TestCase.class"));
     }
-    diagnostics.assertEmpty();
+    diagnostics.assertNoMessages();
   }
 
   @Test
@@ -77,27 +91,36 @@
     Box<String> mainDexList = new Box<>();
     // Build a app with a class extending InstrumentationTestCase and including both the junit
     // and the Android library.
-    CollectingDiagnosticHandler diagnostics = new CollectingDiagnosticHandler();
-    R8Command.Builder builder = new CompatProguardCommandBuilder(true, diagnostics);
-    buildInstrumentationTestCaseApplication(builder);
-    R8Command command =
-        builder
-            .setMinApiLevel(AndroidApiLevel.K.getLevel())
-            // TODO(72793900): This should not be required.
-            .addProguardConfiguration(ImmutableList.of("-keep class ** { *; }"), Origin.unknown())
-            .addProguardConfiguration(ImmutableList.of("-dontobfuscate"), Origin.unknown())
-            .addMainDexRules(keepInstrumentationTestCaseRules, Origin.unknown())
-            .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
-            .setMainDexListConsumer(ToolHelper.consumeString(mainDexList::set))
-            .build();
-    CodeInspector inspector = new CodeInspector(ToolHelper.runR8(command));
-    assertTrue(inspector.clazz("instrumentationtest.InstrumentationTest").isPresent());
-    assertEquals(
-        !lookupLibraryBeforeProgram, mainDexList.get().contains("junit/framework/TestCase.class"));
-    assertEquals(
-        lookupLibraryBeforeProgram ? 0 : 1,
-        diagnostics.countLibraryClassExtendsProgramClassWarnings(
-            "android.test.InstrumentationTestCase", "junit.framework.TestCase"));
+    testForR8Compat(Backend.DEX)
+        .apply(b -> buildInstrumentationTestCaseApplication(b.getBuilder()))
+        .setMinApi(AndroidApiLevel.K)
+        // TODO(72793900): This should not be required.
+        .addKeepRules(ImmutableList.of("-keep class ** { *; }"))
+        .addDontObfuscate()
+        .addMainDexRules(keepInstrumentationTestCaseRules)
+        .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+        .setMainDexListConsumer(ToolHelper.consumeString(mainDexList::set))
+        .allowDiagnosticMessages()
+        .compileWithExpectedDiagnostics(
+            diagnostics -> {
+              diagnostics
+                  .assertNoErrors()
+                  .assertAllInfosMatch(
+                      diagnosticType(DuplicateTypeInProgramAndLibraryDiagnostic.class));
+              assertEquals(
+                  lookupLibraryBeforeProgram ? 0 : 1,
+                  countLibraryClassExtendsProgramClassWarnings(
+                      diagnostics.getWarnings(),
+                      "android.test.InstrumentationTestCase",
+                      "junit.framework.TestCase"));
+            })
+        .inspect(
+            inspector -> {
+              assertTrue(inspector.clazz("instrumentationtest.InstrumentationTest").isPresent());
+              assertEquals(
+                  !lookupLibraryBeforeProgram,
+                  mainDexList.get().contains("junit/framework/TestCase.class"));
+            });
   }
 
   @Test
@@ -108,49 +131,23 @@
         .setMinApi(AndroidApiLevel.B)
         .addMainDexRules(keepInstrumentationTestCaseRules)
         .compile()
-        // Library types and method overrides are lazily enqueued, thus no longer causing failures.
+        // Library types and method overrides are lazily enqueued, thus no warnings/errors.
         .assertNoMessages();
   }
 
-  private static class CollectingDiagnosticHandler implements DiagnosticsHandler {
-    private final List<Diagnostic> infos = new ArrayList<>();
-    private final List<Diagnostic> warnings = new ArrayList<>();
-    private final List<Diagnostic> errors = new ArrayList<>();
+  private static boolean isLibraryClassExtendsProgramClassWarning(
+      String libraryClass, String programClass, Diagnostic diagnostic) {
+    return diagnostic
+        .getDiagnosticMessage()
+        .equals("Library class " + libraryClass + " extends program class " + programClass);
+  }
 
-    @Override
-    public void info(Diagnostic info) {
-      infos.add(info);
-    }
-
-    @Override
-    public void warning(Diagnostic warning) {
-      warnings.add(warning);
-    }
-
-    @Override
-    public void error(Diagnostic error) {
-      errors.add(error);
-    }
-
-    public void assertEmpty() {
-      assertEquals(0, errors.size());
-      assertEquals(0, warnings.size());
-      assertEquals(0, infos.size());
-    }
-
-    private boolean isLibraryClassExtendsProgramClassWarning(
-        String libraryClass, String programClass, Diagnostic diagnostic) {
-      return diagnostic.getDiagnosticMessage().equals(
-          "Library class "+ libraryClass + " extends program class " + programClass);
-    }
-
-    public long countLibraryClassExtendsProgramClassWarnings(
-        String libraryClass, String programClass) {
-      return warnings.stream()
-          .filter(
-              diagnostics ->
-                  isLibraryClassExtendsProgramClassWarning(libraryClass, programClass, diagnostics))
-          .count();
-    }
+  public static long countLibraryClassExtendsProgramClassWarnings(
+      List<Diagnostic> diagnostics, String libraryClass, String programClass) {
+    return diagnostics.stream()
+        .filter(
+            diagnostic ->
+                isLibraryClassExtendsProgramClassWarning(libraryClass, programClass, diagnostic))
+        .count();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageObjectOnProgramPathTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageObjectOnProgramPathTest.java
index a4c60cd..da7342c 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageObjectOnProgramPathTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageObjectOnProgramPathTest.java
@@ -40,7 +40,7 @@
         .addKeepMainRule(Main.class)
         .addDontWarn("*")
         .addKeepClassRules(Object.class)
-        .allowDiagnosticWarningMessages()
+        .allowDiagnosticMessages()
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("A::foo");
   }