Support diagnostics mapping in tracereferences

Bug: 169127026
Bug: 169546956
Change-Id: I523e29f8376f4502dd87963089f9ad76ce992717
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
index 45878c9..ca4e9e4 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
+import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import java.io.IOException;
 import java.nio.file.Files;
@@ -44,7 +45,7 @@
           "                          # the number will be based on heuristics taking the number",
           "                          # of cores into account.");
 
-  static final Iterable<String> MAP_DIAGNOSTICS_USAGE_MESSAGE =
+  public static final Iterable<String> MAP_DIAGNOSTICS_USAGE_MESSAGE =
       Arrays.asList(
           "  " + MAP_DIAGNOSTICS + "[:<type>] <from-level> <to-level>",
           "                          # Map diagnostics of <type> (default any) reported as",
@@ -152,7 +153,13 @@
     }
   }
 
-  private DiagnosticsLevel tryParseLevel(B builder, String arg, Origin origin) {
+  int tryParseMapDiagnostics(B builder, String arg, String[] args, int argsIndex, Origin origin) {
+    return tryParseMapDiagnostics(
+        builder::error, builder.getReporter(), arg, args, argsIndex, origin);
+  }
+
+  private static DiagnosticsLevel tryParseLevel(
+      Consumer<Diagnostic> errorHandler, String arg, Origin origin) {
     if (arg.equals("error")) {
       return DiagnosticsLevel.ERROR;
     }
@@ -162,7 +169,7 @@
     if (arg.equals("info")) {
       return DiagnosticsLevel.INFO;
     }
-    builder.error(
+    errorHandler.accept(
         new StringDiagnostic(
             "Invalid diagnostics level '"
                 + arg
@@ -172,28 +179,34 @@
     return null;
   }
 
-  int tryParseMapDiagnostics(B builder, String arg, String[] args, int argsIndex, Origin origin) {
+  public static int tryParseMapDiagnostics(
+      Consumer<Diagnostic> errorHandler,
+      Reporter reporter,
+      String arg,
+      String[] args,
+      int argsIndex,
+      Origin origin) {
     if (!arg.startsWith(MAP_DIAGNOSTICS)) {
       return -1;
     }
     if (args.length <= argsIndex + 2) {
-      builder.error(new StringDiagnostic("Missing argument(s) for " + arg + ".", origin));
+      errorHandler.accept(new StringDiagnostic("Missing argument(s) for " + arg + ".", origin));
       return args.length - argsIndex;
     }
     String remaining = arg.substring(MAP_DIAGNOSTICS.length());
     String diagnosticsClassName = "";
     if (remaining.length() > 0) {
       if (remaining.length() == 1 || remaining.charAt(0) != ':') {
-        builder.error(
+        errorHandler.accept(
             new StringDiagnostic("Invalid diagnostics type specification " + arg + ".", origin));
         return 0;
       }
       diagnosticsClassName = remaining.substring(1);
     }
-    DiagnosticsLevel from = tryParseLevel(builder, args[argsIndex + 1], origin);
-    DiagnosticsLevel to = tryParseLevel(builder, args[argsIndex + 2], origin);
+    DiagnosticsLevel from = tryParseLevel(errorHandler, args[argsIndex + 1], origin);
+    DiagnosticsLevel to = tryParseLevel(errorHandler, args[argsIndex + 2], origin);
     if (from != null && to != null) {
-      builder.getReporter().addDiagnosticsLevelMapping(from, diagnosticsClassName, to);
+      reporter.addDiagnosticsLevelMapping(from, diagnosticsClassName, to);
     }
     return 2;
   }
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
index 9b95dca..f299459 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.tracereferences;
 
+import com.android.tools.r8.BaseCompilerCommandParser;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.JdkClassFileProvider;
 import com.android.tools.r8.origin.Origin;
@@ -11,6 +12,7 @@
 import com.android.tools.r8.utils.FlagFile;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import java.io.IOException;
 import java.io.PrintStream;
 import java.nio.file.Files;
@@ -27,20 +29,24 @@
   static final String USAGE_MESSAGE =
       String.join(
           "\n",
-          Arrays.asList(
-              "Usage: tracereferences [options] [@<argfile>]",
-              " Each <argfile> is a file containing additional arguments (one per line)",
-              " and options are:",
-              "  --lib <file|jdk-home>   # Add <file|jdk-home> runtime library.",
-              "  --source <file>         # Add <file> as a source for tracing references.",
-              "  [--target <file>]       # Add <file> as a target for tracing references. When",
-              "                          # target is not specified all references from source",
-              "                          # outside of library are treated as a missing references.",
-              "  [--format printuses|keep|keepallowobfuscation]",
-              "                          # Format of the output. Default is 'printuses'.",
-              "  --output <file>         # Output result in <outfile>.",
-              "  --version               # Print the version of tracereferences.",
-              "  --help                  # Print this message."));
+          Iterables.concat(
+              Arrays.asList(
+                  "Usage: tracereferences [options] [@<argfile>]",
+                  " Each <argfile> is a file containing additional arguments (one per line)",
+                  " and options are:",
+                  "  --lib <file|jdk-home>   # Add <file|jdk-home> runtime library.",
+                  "  --source <file>         # Add <file> as a source for tracing references.",
+                  "  [--target <file>]       # Add <file> as a target for tracing references. When",
+                  "                          # target is not specified all references from source",
+                  "                          # outside of library are treated as a missing"
+                      + " references.",
+                  "  [--format printuses|keep|keepallowobfuscation]",
+                  "                          # Format of the output. Default is 'printuses'.",
+                  "  --output <file>         # Output result in <outfile>."),
+              BaseCompilerCommandParser.MAP_DIAGNOSTICS_USAGE_MESSAGE,
+              Arrays.asList(
+                  "  --version               # Print the version of tracereferences.",
+                  "  --help                  # Print this message.")));
   /**
    * Parse the tracereferences command-line.
    *
@@ -120,6 +126,13 @@
       } else if (arg.startsWith("@")) {
         builder.error(new StringDiagnostic("Recursive @argfiles are not supported: ", origin));
       } else {
+        int argsConsumed =
+            BaseCompilerCommandParser.tryParseMapDiagnostics(
+                builder::error, builder.getReporter(), arg, expandedArgs, i, origin);
+        if (argsConsumed >= 0) {
+          i += argsConsumed;
+          continue;
+        }
         builder.error(new StringDiagnostic("Unsupported argument '" + arg + "'"));
       }
     }
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
index 430af64..5ea9bd0 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
@@ -341,6 +341,47 @@
     }
   }
 
+  @Test
+  public void testMissingReference_errorToWarning() throws Throwable {
+    Path dir = temp.newFolder().toPath();
+    Path targetJar = dir.resolve("target.jar");
+    Path sourceJar = dir.resolve("source.jar");
+    Path output = dir.resolve("output.txt");
+    ZipUtils.zip(
+        targetJar,
+        ToolHelper.getClassPathForTests(),
+        ToolHelper.getClassFileForTestClass(OtherTarget.class));
+    ZipUtils.zip(
+        sourceJar,
+        ToolHelper.getClassPathForTests(),
+        ToolHelper.getClassFileForTestClass(Source.class));
+    DiagnosticsChecker diagnosticsChecker = new DiagnosticsChecker();
+    TraceReferences.run(
+        TraceReferencesCommand.parse(
+                new String[] {
+                  "--lib",
+                  ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
+                  "--target",
+                  targetJar.toString(),
+                  "--source",
+                  sourceJar.toString(),
+                  "--output",
+                  output.toString(),
+                  "--format",
+                  formatName(OutputFormat.PRINTUSAGE),
+                  "--map-diagnostics:MissingDefinitionsDiagnostic",
+                  "error",
+                  "warning"
+                },
+                Origin.unknown(),
+                diagnosticsChecker)
+            .build());
+
+    assertEquals(0, diagnosticsChecker.errors.size());
+    assertEquals(1, diagnosticsChecker.warnings.size());
+    assertEquals(0, diagnosticsChecker.infos.size());
+  }
+
   public static void zip(Path zipFile, String path, byte[] data) throws IOException {
     try (ZipOutputStream stream =
         new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(zipFile)))) {