Reproduce failure when library desugaring already desugared cf input

This also introduce test of different ways to get the desugared library
mismatch error.

Bug: 190075882
Change-Id: I572c9dadfce1a0f78e238ab3b4cbc4a047926ef8
diff --git a/src/main/java/com/android/tools/r8/dex/Marker.java b/src/main/java/com/android/tools/r8/dex/Marker.java
index c915fe7..b892c4a 100644
--- a/src/main/java/com/android/tools/r8/dex/Marker.java
+++ b/src/main/java/com/android/tools/r8/dex/Marker.java
@@ -104,7 +104,7 @@
     }
 
     if (desugaredLibraryIdentifiers.size() > 1) {
-      reporter.error(new DesugaredLibraryMismatchDiagnostic(desugaredLibraryIdentifiers));
+      reporter.error(new DesugaredLibraryMismatchDiagnostic(desugaredLibraryIdentifiers, markers));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/errors/DesugaredLibraryMismatchDiagnostic.java b/src/main/java/com/android/tools/r8/errors/DesugaredLibraryMismatchDiagnostic.java
index ba9af51..5141753 100644
--- a/src/main/java/com/android/tools/r8/errors/DesugaredLibraryMismatchDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/errors/DesugaredLibraryMismatchDiagnostic.java
@@ -5,16 +5,21 @@
 package com.android.tools.r8.errors;
 
 import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 public class DesugaredLibraryMismatchDiagnostic implements Diagnostic {
 
   private final Set<String> desugaredLibraryIdentifiers;
+  private final Set<Marker> markers;
 
-  public DesugaredLibraryMismatchDiagnostic(Set<String> desugaredLibraryIdentifiers) {
+  public DesugaredLibraryMismatchDiagnostic(
+      Set<String> desugaredLibraryIdentifiers, Set<Marker> markers) {
     this.desugaredLibraryIdentifiers = desugaredLibraryIdentifiers;
+    this.markers = markers;
   }
 
   @Override
@@ -31,6 +36,8 @@
   public String getDiagnosticMessage() {
     return "The compilation is merging inputs with different desugared library desugaring "
         + desugaredLibraryIdentifiers
-        + ", which may lead to unexpected runtime errors.";
+        + ", which may lead to unexpected runtime errors."
+        + "The markers are "
+        + markers.stream().map(Marker::toString).collect(Collectors.joining(", "));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java
new file mode 100644
index 0000000..3e8c332
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java
@@ -0,0 +1,292 @@
+// 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.desugar.desugaredlibrary;
+
+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.CompilationFailedException;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.errors.DesugaredLibraryMismatchDiagnostic;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.nio.file.Path;
+import java.util.Collection;
+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 DesugaredLibraryMismatchTest extends DesugaredLibraryTestBase {
+
+  private final TestParameters parameters;
+  private final AndroidApiLevel apiLevel;
+
+  @Parameters(name = "API level: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withNoneRuntime().build(),
+        new AndroidApiLevel[] {
+          AndroidApiLevel.LATEST, AndroidApiLevel.O, AndroidApiLevel.N_MR1, AndroidApiLevel.B
+        });
+  }
+
+  public DesugaredLibraryMismatchTest(TestParameters parameters, AndroidApiLevel apiLevel) {
+    this.parameters = parameters;
+    this.apiLevel = apiLevel;
+  }
+
+  @Test
+  public void testInputDexed() throws Exception {
+    // DEX code without library desugaring.
+    Path libraryDex =
+        testForD8(Backend.DEX)
+            .addProgramClasses(Library.class)
+            .setMinApi(apiLevel)
+            .compile()
+            .writeToZip();
+
+    // Combine DEX input without library desugaring with dexing with library desugaring.
+    try {
+      testForD8()
+          .addProgramFiles(libraryDex)
+          .addProgramClasses(TestRunner.class)
+          .setMinApi(apiLevel)
+          .enableCoreLibraryDesugaring(apiLevel)
+          .compileWithExpectedDiagnostics(
+              diagnostics -> {
+                diagnostics.assertNoInfos();
+                diagnostics.assertAllWarningsMatch(
+                    diagnosticMessage(
+                        containsString(
+                            "The compilation is slowed down due to a mix of class file and dex"
+                                + " file inputs in the context of desugared library.")));
+                if (apiLevel.isLessThan(AndroidApiLevel.O)) {
+                  diagnostics.assertErrorsMatch(
+                      diagnosticType(DesugaredLibraryMismatchDiagnostic.class));
+                } else {
+                  diagnostics.assertNoMessages();
+                }
+              });
+
+    } catch (CompilationFailedException e) {
+    }
+  }
+
+  @Test
+  public void testInputCfDesugared() throws Exception {
+    // CF to CF desugared code without library desugaring.
+    Path desugaredLibrary =
+        testForD8(Backend.CF)
+            .addProgramClasses(Library.class)
+            .setMinApi(apiLevel)
+            .compile()
+            .writeToZip();
+
+    // Combine CF desugared input without library desugaring with dexing with library desugaring.
+    try {
+      testForD8()
+          .addProgramFiles(desugaredLibrary)
+          .addProgramClasses(TestRunner.class)
+          .setMinApi(apiLevel)
+          .enableCoreLibraryDesugaring(apiLevel)
+          .compileWithExpectedDiagnostics(
+              diagnostics -> {
+                if (apiLevel.isLessThan(AndroidApiLevel.O)) {
+                  // This is the failure reported in b/190075882.
+                  diagnostics.assertOnlyErrors();
+                  diagnostics.assertErrorsMatch(
+                      diagnosticType(DesugaredLibraryMismatchDiagnostic.class));
+                } else {
+                  diagnostics.assertNoMessages();
+                }
+              });
+
+    } catch (CompilationFailedException e) {
+    }
+  }
+
+  @Test
+  public void testInputCfDesugaredAndDexed() throws Exception {
+    // CF to CF desugared code without library desugaring.
+    Path desugaredLibrary =
+        testForD8(Backend.CF)
+            .addProgramClasses(Library.class)
+            .setMinApi(apiLevel)
+            .compile()
+            .writeToZip();
+
+    // CF to CF desugared code without library desugaring compiled to DEX.
+    Path desugaredLibraryDex =
+        testForD8(Backend.DEX)
+            .addProgramFiles(desugaredLibrary)
+            .setMinApi(apiLevel)
+            .disableDesugaring()
+            .compile()
+            .writeToZip();
+
+    // Combine DEX input without library desugaring with dexing with library desugaring.
+    try {
+      testForD8()
+          .addProgramFiles(desugaredLibraryDex)
+          .addProgramClasses(TestRunner.class)
+          .setMinApi(apiLevel)
+          .enableCoreLibraryDesugaring(apiLevel)
+          .compileWithExpectedDiagnostics(
+              diagnostics -> {
+                diagnostics.assertNoInfos();
+                diagnostics.assertAllWarningsMatch(
+                    diagnosticMessage(
+                        containsString(
+                            "The compilation is slowed down due to a mix of class file and dex"
+                                + " file inputs in the context of desugared library.")));
+                if (apiLevel.isLessThan(AndroidApiLevel.O)) {
+                  diagnostics.assertErrorsMatch(
+                      diagnosticType(DesugaredLibraryMismatchDiagnostic.class));
+                } else {
+                  diagnostics.assertNoMessages();
+                }
+              });
+
+    } catch (CompilationFailedException e) {
+    }
+  }
+
+  @Test
+  public void testCfInputLibraryDesugared() throws Exception {
+    // CF to CF desugared code with library desugaring.
+    Path desugaredLibrary =
+        testForD8(Backend.CF)
+            .addProgramClasses(Library.class)
+            .setMinApi(apiLevel)
+            .enableCoreLibraryDesugaring(apiLevel)
+            .compile()
+            .writeToZip();
+
+    // Combine CF input with library desugaring with dexing without library desugaring.
+    try {
+      testForD8()
+          .addProgramFiles(desugaredLibrary)
+          .addProgramClasses(TestRunner.class)
+          .setMinApi(apiLevel)
+          .compileWithExpectedDiagnostics(
+              diagnostics -> {
+                if (apiLevel.isLessThan(AndroidApiLevel.O)) {
+                  diagnostics.assertOnlyErrors();
+                  diagnostics.assertErrorsMatch(
+                      diagnosticType(DesugaredLibraryMismatchDiagnostic.class));
+                } else {
+                  diagnostics.assertNoMessages();
+                }
+              });
+    } catch (CompilationFailedException e) {
+    }
+  }
+
+  @Test
+  public void testMergeLibraryDesugaredWithNotLibraryDesugared() throws Exception {
+    // DEX code with library desugaring.
+    Path libraryDex =
+        testForD8(Backend.DEX)
+            .addProgramClasses(Library.class)
+            .setMinApi(apiLevel)
+            .enableCoreLibraryDesugaring(apiLevel)
+            .compile()
+            .writeToZip();
+
+    // DEX code without library desugaring.
+    Path programDex =
+        testForD8(Backend.DEX)
+            .addProgramClasses(TestRunner.class)
+            .setMinApi(apiLevel)
+            .compile()
+            .writeToZip();
+
+    try {
+      testForD8()
+          .addProgramFiles(libraryDex)
+          .addProgramFiles(programDex)
+          .setMinApi(apiLevel)
+          .compileWithExpectedDiagnostics(
+              diagnostics -> {
+                if (apiLevel.isLessThan(AndroidApiLevel.O)) {
+                  diagnostics.assertOnlyErrors();
+                  diagnostics.assertErrorsMatch(
+                      diagnosticType(DesugaredLibraryMismatchDiagnostic.class));
+                } else {
+                  diagnostics.assertNoMessages();
+                }
+              });
+    } catch (CompilationFailedException e) {
+    }
+  }
+
+  @Test
+  public void testMergeDifferentLibraryDesugarVersions() throws Exception {
+    // DEX code with library desugaring using a desugared library configuration with a
+    // different identifier.
+    String identifier = "my-identifier";
+    Path libraryDex =
+        testForD8(Backend.DEX)
+            .applyIf(
+                apiLevel.isLessThan(AndroidApiLevel.O),
+                builder ->
+                    builder.addOptionsModification(
+                        options ->
+                            options.desugaredLibraryConfiguration =
+                                DesugaredLibraryConfiguration.builder(
+                                        options.dexItemFactory(),
+                                        options.reporter,
+                                        Origin.unknown())
+                                    .setDesugaredLibraryIdentifier(identifier)
+                                    .build()))
+            .addProgramClasses(Library.class)
+            .setMinApi(apiLevel)
+            .enableCoreLibraryDesugaring(apiLevel)
+            .compile()
+            .writeToZip();
+
+    // DEX code without library desugaring.
+    Path programDex =
+        testForD8(Backend.DEX)
+            .addProgramClasses(TestRunner.class)
+            .setMinApi(apiLevel)
+            .compile()
+            .writeToZip();
+
+    try {
+      testForD8()
+          .addProgramFiles(libraryDex)
+          .addProgramFiles(programDex)
+          .setMinApi(apiLevel)
+          .compileWithExpectedDiagnostics(
+              diagnostics -> {
+                if (apiLevel.isLessThan(AndroidApiLevel.O)) {
+                  diagnostics.assertOnlyErrors();
+                  diagnostics.assertErrorsMatch(
+                      allOf(
+                          diagnosticType(DesugaredLibraryMismatchDiagnostic.class),
+                          diagnosticMessage(containsString(identifier))));
+                } else {
+                  diagnostics.assertNoMessages();
+                }
+              });
+    } catch (CompilationFailedException e) {
+    }
+  }
+
+  static class Library {}
+
+  static class TestRunner {
+
+    public static void main(String[] args) {
+      System.out.println(Library.class);
+    }
+  }
+}